#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>

/*
建立套接字 scocket   返回sockfd
int socket(int domain, int type, int protocol);
    domain 网络协议 AF_INET    AF_INET6
    type  SOCK_STREAM 选择TCP   SOCK_DGRAM 选择UDP

绑定自己的IP地址  和 端口号   0、-1
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    sockfd  套接字描述符
    addr    结构体sockaddr的指针 ip和端口
    addrlen  结构体的大小

listen()声明sockfd处于监听状态  0、-1
int listen(int sockfd, int backlog);
    sockfd ： 套接字文件描述符
    backlog  ：同时  最多支持连接上来的客户端总数


等待接受客户端连接  失败-1  返回已连接的套接字文件描述符
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    sockfd： 套接字文件描述符
    addr  ：  连接上来的客户端的IP地址和端口号
    addrlen ： 长度

接收数据  失败-1  返回0 表示客户端断开连接
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    sockfd： 已连接的套接字文件描述符  connectfd
    buf  ： 接收到的数据存储到这里
    len  ：  接收数据的大小
    flags  ：  一般设置为  0


int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
*/
pthread_mutex_t mutex;
pthread_cond_t cond;
int count_exit = 0;

int sockfd;
struct sockaddr_in ClientAddrArray[10];
int ClientAddrArray_len;
int ClientAddr_len = sizeof(struct sockaddr_in);

void *sendto_routine(void *arg)
{

    printf("服务器 sendto_routine start\n");

    while (1)
    {
        char buf[1024] = {0};
        int index;
        printf("请输入要发送的客户端的下标：\n");
        scanf("%d", &index);

        printf("请输入要发送的数据：\n");
        scanf("%s", buf); // 阻塞

        ssize_t send_res = sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&ClientAddrArray[index], ClientAddr_len);
        if (send_res < 0)
        {
            perror("send error");
            return NULL;
        }
        printf("服务器 send ok... %ld %s\n", send_res, buf);
    }

    pthread_mutex_lock(&mutex);
    count_exit++;
    pthread_mutex_unlock(&mutex);
    pthread_cond_broadcast(&cond); // 唤醒所有等待的线程
    // printf("服务器 sendto_routine end\n");
}
void *recvfrom_routine(void *arg)
{

    printf("服务器 recvfrom_routine start\n");

    while (1)
    {
        // 接收数据
        char buf[1024] = {0};
        int recv_res = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&ClientAddrArray[ClientAddrArray_len], &ClientAddr_len);
        if (recv_res < 0)
        {
            perror("recv error");
            continue;
        }
        if (recv_res == 0)
        {
            printf("client close\n");
            break;
        }
        if (strcmp(buf, "exit\n") == 0)
        {
            printf("client close\n");
            break;
        }
        // 这里理论是写在tcp里面的  这里没有tcp类似的 链接概念
        printf("服务器 recv message 第%d个客户端: %s from ip:%s prot:%d\n", ClientAddrArray_len, buf, inet_ntoa(ClientAddrArray[ClientAddrArray_len].sin_addr), ntohs(ClientAddrArray[ClientAddrArray_len].sin_port));
        ClientAddrArray_len++;
    }

    pthread_mutex_lock(&mutex);
    count_exit++;
    pthread_mutex_unlock(&mutex);
    pthread_cond_broadcast(&cond); // 唤醒所有等待的线程

    // printf("服务器 recvfrom_routine end\n");
}

int main()
{
    // 创建套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket error");
        return -1;
    }
    printf("socket ok...\n");

    // 创建Server端地址结构体
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;    // 使用ipv4
    addr.sin_port = htons(11112); // 端口   htons()  主机字节序转网络字节序host to net
    // addr.sin_addr.s_addr = inet_addr("192.168.46.2"); // ip  inet_addr() 将主机IP 转成 网络IP
    addr.sin_addr.s_addr = inet_addr("0.0.0.0"); // ip  inet_addr() 将主机IP 转成 网络IP
    // 0.0.0.0  表示本机所有网卡的ip

    // 绑定
    int bind_res = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
    if (bind_res < 0)
    {
        perror("bind error");
        return -1;
    }
    printf("bind ok...\n");

    // 创建Client端地址结构体
    struct sockaddr_in ClientAddr;
    memset(&ClientAddr, 0, sizeof(addr));
    ClientAddr_len = sizeof(ClientAddr);

    printf("等待客户端连接...\n");

    // 创建一个线程
    pthread_t thread_sendto;
    int res_thread_sendto = pthread_create(&thread_sendto, NULL, sendto_routine, NULL);
    printf("res_thread_sendto=%d  thread_sendto=%lu\n", res_thread_sendto, thread_sendto);

    // 创建一个线程
    pthread_t thread_recvfrom;
    int res_thread_recvfrom = pthread_create(&thread_recvfrom, NULL, recvfrom_routine, NULL);
    printf("res_thread_recvfrom=%d  thread_recvfrom=%lu\n", res_thread_recvfrom, thread_recvfrom);

    pthread_mutex_lock(&mutex); // 上锁
    while (count_exit < 1)
    {
        printf("没有线程退出\n");
        pthread_cond_wait(&cond, &mutex); // 等待条件满足 阻塞 自动解锁
    }
    pthread_mutex_unlock(&mutex); // 解锁

    // 有线程退出了
    pthread_cancel(thread_recvfrom);
    pthread_cancel(thread_sendto);

    // 阻塞等待线程结束
    pthread_join(thread_recvfrom, NULL);
    printf("服务器 thread_recvfrom end\n");

    pthread_join(thread_sendto, NULL); // 阻塞的
    printf("服务器 thread_sendto end\n");

    close(sockfd);
    printf("close sockfd ok...\n");
    return 0;
}
// 模拟tcp客户端  nc 192.168.46.2 11111
// 模拟tcp服务端  nc -l 11111
// 模拟udp客户端  nc -u 192.168.46.2 11111
// 模拟udp服务端  nc -lu 11111
